﻿using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using Net.Adapter;
using Net.Config;
using Net.Event;
using Net.Helper;
using Net.Server;
using Net.Share;
using Net.System;

namespace Net
{
    /// <summary>
    /// 统一网络客户端基类
    /// </summary>
    public abstract partial class ClientUnitBase : IDisposable
    {
        #region 字段
        protected int networkFlowHandlerID;
        protected int keepAliveHandlerID;
        protected int perSecondHandlerID;
        protected readonly MyDictionary<int, StreamData> streamDatas = new();
        #endregion

        #region 属性
        /// <summary>
        /// 玩家名称
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// Tcp套接字
        /// </summary>
        public Socket Client { get; set; }
        /// <summary>
        /// 存储客户端终端
        /// </summary>
        public EndPoint RemotePoint { get; set; }
        /// <summary>
        /// 保活心跳可发送时间
        /// </summary>
        protected int KeepAliveTimeout;
        /// <summary>
        /// 保活计数
        /// </summary>
        protected int KeepAliveCount;
        /// <summary>
        /// tcp叠包状态码 0:正常 >=1:叠包次数
        /// </summary>
        protected int overlapPack;
        protected int overlapPackOffset;
        protected int overlapPackLength;
        protected ISegment overlapPackStream;
        /// <summary>
        /// 用户唯一身份标识
        /// </summary>
        public long UserId { get; set; }
        /// <summary>
        /// 远程套接字Id
        /// </summary>
        public long RemoteId => RemotePoint.GetRemoteId();
        /// <summary>
        /// 数据模型队列
        /// </summary>
        public QueueSafe<IRpcModel> RpcQueue = new();
        /// <summary>
        /// 是否已经被释放?
        /// </summary>
        public bool IsDispose { get; internal set; }
        /// <summary>
        /// 是否处于连接
        /// </summary>
        public bool Connected { get; set; }
        /// <summary>
        /// 客户端连接时间
        /// </summary>
        public DateTime ConnectTime { get; set; }

        /// <summary>
        /// 此客户端接收到的字节总量
        /// </summary>
        public long BytesReceived { get; set; }
        /// <summary>
        /// 基类配置
        /// </summary>
        internal protected ConfigBase config;
        /// <summary>
        /// 序列化适配器
        /// </summary>
        public ISerializeAdapter SerializeAdapter { get; set; } = new SerializeDefaultAdapter();
        /// <summary>
        /// 数据包适配器
        /// </summary>
        public IPackageAdapter PackageAdapter { get; set; } = new PackageAdapter();
        /// <summary>
        /// Rpc适配器
        /// </summary>
        public IRpcAdapter RpcAdapter { get; set; } = new NormalRpcAdapter() { Mode = AdapterMode.Client };
        /// <summary>
        /// 远程调用工厂接口
        /// </summary>
        public IRpcModelFactory RpcUnitFactory { get; set; } = new RpcModelFactory();
        internal protected int performance;
        /// <summary>
        /// 客户端性能模式
        /// </summary>
        public Performance Performance
        {
            get => (Performance)performance;
            set => performance = (int)value;
        }
        /// <summary>
        /// 4个字节记录数据长度 + 1CRC校验
        /// </summary>
        public virtual int Frame { get; internal set; } = 5;
        /// <summary>
        /// 流量统计字段
        /// </summary>
        internal protected NetworkTrafficStatistics trafficStatistics;
        /// <summary>
        /// 网络循环事件处理
        /// </summary>
        protected TimerEvent NetworkLoop = new();

        /// <summary>
        /// CRC校验错误次数, 如果有错误每秒提示一次
        /// </summary>
        public int CRCError { get; set; }
        /// <summary>
        /// 发送窗口已满提示次数
        /// </summary>
        public int WindowFullError { get; set; }
        /// <summary>
        /// 数据大小错误, 数据被拦截修改或者其他问题导致错误
        /// </summary>
        public int DataSizeError { get; set; }
        /// <summary>
        /// 数据队列溢出错误, 当要发送的数据队列堆积到<see cref="ServerBase{Player, Scene}.LimitQueueCount"/> 后提示错误
        /// </summary>
        public int DataQueueOverflowError { get; set; }
        /// <summary>
        /// 大数据传输缓存最大长度错误次数, 请在<see cref="ServerBase{Player, Scene}.BigDataCacheLength"/>设置最大缓存长度
        /// </summary>
        public int BigDataCacheLengthError { get; set; }
        /// <summary>
        /// 协议出错次数，这是解析数据包时协议码不正确时提示
        /// </summary>
        public int ProtocolError { get; internal set; }
        #endregion

        #region 事件
        /// <summary>
        /// ping服务器回调 参数double为延迟毫秒单位 当<see cref="RTOMode"/>=<see cref="RTOMode.Variable"/>可变重传时, 内核将会每秒自动ping一次
        /// </summary>
        public Action<uint> OnPingCallback;
        /// <summary>
        /// 当socket发送失败调用.参数1:玩家对象, 参数2:发送的字节数组, 参数3:发送标志(可靠和不可靠)  ->可通过<see cref="SendByteData"/>方法重新发送
        /// </summary>
        public Action<ISegment> OnSendErrorHandle;
        /// <summary>
        /// 当接收数据
        /// </summary>
        public OnReceiveDataHandle OnReceiveData { get; set; }
        /// <summary>
        /// 当执行调用远程过程方法时触发
        /// </summary>
        public OnReceiveDataHandle OnRpcInvoke { get; set; }
        /// <summary>
        /// 当开始下载文件时调用, 参数1(string):服务器发送的文件名 返回值(string):开发者指定保存的文件路径(全路径名称)
        /// </summary>
        public Func<string, string> OnDownloadFile { get; set; }
        /// <summary>
        /// 当服务器发送的文件完成, 接收到文件后调用, 返回true:框架内部释放文件流和删除临时文件(默认) false:使用者处理
        /// </summary>
        public Func<StreamData, bool> OnReceiveFile { get; set; }
        /// <summary>
        /// 当接收到发送的文件进度
        /// </summary>
        public Action<StreamProgress> OnReceiveFileProgress { get; set; }
        /// <summary>
        /// 当发送的文件进度
        /// </summary>
        public Action<StreamProgress> OnSendFileProgress { get; set; }
        #endregion

        #region 客户端释放内存
        /// <summary>
        /// 析构网络客户端
        /// </summary>
        ~ClientUnitBase()
        {
            Dispose();
        }

        /// <summary>
        /// 释放
        /// </summary>
        public virtual void Dispose()
        {
            if (IsDispose)
                return;
            IsDispose = true;
            Client?.Close();
            overlapPackStream?.Dispose();
            overlapPackStream = null;
            overlapPack = 0;
            overlapPackOffset = 0;
            overlapPackLength = 0;
            Connected = false;
            RpcQueue = new();
        }
        #endregion

        #region 客户端数据处理函数

        /// <summary>
        /// 当接收到客户端数据
        /// </summary>
        /// <param name="model"></param>
        /// <param name="segment"></param>
        protected virtual void OnReceiveDataHandler(IRpcModel model, ISegment segment)
        {
            if (model.Protocol != 0)
                OnRpcHandler(model);
            else
                OnReceiveData?.Invoke(model);
        }
        /// <summary>
        /// 当Rpc处理
        /// </summary>
        /// <param name="model"></param>
        protected virtual void OnRpcHandler(IRpcModel model) => OnRpcInvoke?.Invoke(model);
        #endregion

        #region 客户端处理
        protected virtual void SetDataHead(ISegment stream)
        {
            stream.Position = Frame + PackageAdapter.HeadLength;
        }

        protected virtual void WriteDataBody(ref ISegment stream, int count)
        {
            int index = 0;
            for (int i = 0; i < count; i++)
            {
                if (!RpcQueue.TryDequeue(out IRpcModel model))
                    continue;
                var startPos = stream.Position;
                stream.WriteByte((byte)(model.Kernel ? 68 : 74));
                stream.WriteByte(model.Cmd);
                stream.Write(model.Token);
                var dataSizePos = stream.Position;
                stream.Position += 4;
                if (model.Kernel & model.Serialize)
                {
                    var completed = SerializeAdapter.OnSerializeRpc(stream, model);
                    if (!completed)
                    {
                        stream.Position = startPos;
                        continue;
                    }
                }
                else if (model.Buffer.Length > 0)
                {
                    var len = stream.Position + model.Buffer.Length + Frame;
                    if (len >= stream.Length)
                    {
                        var buffer = stream.ToArray(true);
                        stream = BufferPool.Take(len);
                        stream.Write(buffer, false);
                    }
                    stream.Write(model.Buffer, false);
                }
                var currPos = stream.Position;
                stream.Position = dataSizePos;
                stream.WriteFixed((uint)(currPos - dataSizePos - 4));
                stream.Position = currPos;
                if (++index >= config.PackageLength | currPos + 10240 >= BufferPool.Size)
                    break;
            }
        }

        /// <summary>
        /// 重置头部数据大小, 在小数据达到<see cref="PackageLength"/>以上时会将这部分的数据先发送, 发送后还有连带的数据, 需要重置头部数据,装入大货车
        /// </summary>
        /// <param name="stream"></param>
        protected virtual void ResetDataHead(ISegment stream)
        {
            stream.SetPositionLength(Frame + PackageAdapter.HeadLength);
        }

        /// <summary>
        /// 发送处理
        /// </summary>
        protected virtual void SendDataHandler()
        {
            var count = RpcQueue.Count;
            if (count <= 0)
                return;
            var stream = BufferPool.Take(config.SendBufferSize);
            SetDataHead(stream);
            WriteDataBody(ref stream, count);
            PacketData(stream);
            SendByteData(stream);
            BufferPool.Push(stream);
        }

        protected virtual void PacketData(ISegment stream)
        {
            stream.Flush(false);
            SetDataHead(stream);
            PackageAdapter.Pack(stream);
            var len = stream.Count - Frame;
            var lenBytes = BitConverter.GetBytes(len);
            var crc = CRCHelper.CRC8(lenBytes, 0, lenBytes.Length);
            stream.Position = 0;
            stream.Write(lenBytes, 0, 4);
            stream.WriteByte(crc);
            stream.Position += len;
        }

        protected virtual void SendByteData(ISegment buffer)
        {
            if (!Client.Connected)
                return;
            if (Client.Poll(performance, SelectMode.SelectWrite))
            {
                trafficStatistics.SentPacketsPerSecond++;
                trafficStatistics.SentBytesPerSecond += buffer.Count;
                int count = Client.Send(buffer.Buffer, buffer.Offset, buffer.Count, SocketFlags.None, out SocketError error);
                if (error != SocketError.Success | count <= 0)
                {
                    OnSendErrorHandle?.Invoke(buffer);
                    return;
                }
                else if (count != buffer.Count)
                    NDebug.LogError($"发送了{buffer.Count - count}个字节失败!");
            }
            else
            {
                WindowFullError++;
            }
        }
        #endregion

        #region 网络处理
        internal protected virtual void NetworkInitialize()
        {
            NetworkLoop.RemoveEvent(keepAliveHandlerID);
            NetworkLoop.RemoveEvent(perSecondHandlerID);
            keepAliveHandlerID = NetworkLoop.AddEvent("KeepAliveHandler", config.KeepAliveTimeout, KeepAliveHandler);
            perSecondHandlerID = NetworkLoop.AddEvent("CheckPerSecond", 1f, OnCheckPerSecond);
        }

        protected virtual bool OnCheckPerSecond()
        {
            if (CRCError > 0)
            {
                NDebug.LogError($"[{this}]CRC校验失败! {CRCError}/秒");
                CRCError = 0;
            }
            if (DataQueueOverflowError > 0)
            {
                NDebug.LogError($"[{this}]数据缓存列表超出限制! {DataQueueOverflowError}/秒");
                DataQueueOverflowError = 0;
                var modelsDict = new MyDictionary<IRpcModel, int>();
                foreach (var item in RpcQueue)
                {
                    if (!modelsDict.ContainsKey(item))
                        modelsDict[item] = 0;
                    modelsDict[item]++;
                }
                NDebug.LogError($"[{this}] 以下是堆积了哪些数据:");
                foreach (var item in modelsDict)
                    NDebug.LogError($"[{this}] {item.Key} 相同数据量:{item.Value}");
            }
            if (BigDataCacheLengthError > 0)
            {
                NDebug.LogError($"[{this}]大数据缓存超出限制,在服务器类属性BigDataCacheLength设置大数据缓存最大长度! {BigDataCacheLengthError}/秒");
                BigDataCacheLengthError = 0;
            }
            if (ProtocolError > 0)
            {
                NDebug.LogError($"[{this}][可忽略]协议出错! {ProtocolError}/秒");
                ProtocolError = 0;
            }
            if (WindowFullError > 0)
            {
                NDebug.LogError($"[{this}]发送窗口已满,等待对方接收中! {WindowFullError}/秒");
                WindowFullError = 0;
            }
            if (DataSizeError > 0)
            {
                NDebug.LogError($"[{this}]数据被拦截修改或数据量太大, 如果想传输大数据, 请设置PackageSize属性! {DataSizeError}/秒");
                DataSizeError = 0;
            }
            return true;
        }

        public virtual void OnNetworkTick() => RpcAdapter.Execute();

        public virtual void ReceiveHandler()
        {
            if (Client == null)
                return;
            if (!Client.Connected)
            {
                if (Connected)
                    ConnectLost(SocketError.NotConnected);
                return;
            }
            if (Client.Poll(performance, SelectMode.SelectRead))
            {
                var segment = BufferPool.Take(config.ReceiveBufferSize);
                segment.Count = Client.Receive(segment.Buffer, 0, segment.Length, SocketFlags.None, out SocketError error);
                if (segment.Count == 0 | error != SocketError.Success)
                {
                    BufferPool.Push(segment);
                    if (Connected) //如果是连接完成状态下才能触发连接中断, 要不然还没连接成功就触发连接中断就很不正常
                        ConnectLost(error);
                    return;
                }
                ReceiveHandler(ref segment);
                BufferPool.Push(segment);
            }
        }

        public virtual void ConnectLost(SocketError socketError)
        {
            Connected = false;
        }

        public virtual unsafe void ReceiveHandler(ref ISegment buffer)
        {
            trafficStatistics.ReceivedPacketsPerSecond++;
            trafficStatistics.ReceivedBytesPerSecond += buffer.Count;
            BytesReceived += buffer.Count;
            KeepAliveTimeout = Environment.TickCount + config.KeepAliveTimeout;
            KeepAliveCount = 0;
            if (overlapPack > 0)
            {
                overlapPack++;
                var size = buffer.Count - buffer.Position;
                overlapPackOffset += size;
                overlapPackStream.Write(buffer.Buffer, buffer.Position, size);
                if (overlapPackOffset < overlapPackLength)
                    return;
                BufferPool.Push(buffer);//要回收掉, 否则会提示内存泄露
                buffer = overlapPackStream; //ref 才不会导致提示内存泄露
                buffer.Flush(true);
                overlapPackStream = null; //释放引用
            }
            while (buffer.Position < buffer.Count)
            {
                if (buffer.Position + Frame > buffer.Count)//流数据偶尔小于frame头部字节
                {
                    OverlapPack(buffer, buffer.Position, 0);
                    break;
                }
                var size = PackageAdapter.ReadHead(buffer);
                if (size < 0)
                {
                    overlapPack = 0;
                    if (size == -1)
                        CRCError++;
                    if (size == -2)
                        DataSizeError++;
                    return;
                }
                if (size > config.PackageSize) //如果出现解析的数据包大小有问题，则不处理
                {
                    overlapPack = 0;
                    DataSizeError++;
                    return;
                }
                if (buffer.Position + size <= buffer.Count)
                {
                    overlapPack = 0;
                    var count = buffer.Count;//此长度可能会有连续的数据(粘包)
                    buffer.Count = buffer.Position + size;//需要指定一个完整的数据长度给内部解析
                    OnReceiveDataHandler(buffer, true);
                    buffer.Count = count;//解析完成后再赋值原来的总长
                }
                else if (!IsDispose) //当RemoveClient后导致stackStream=null后报错问题
                {
                    OverlapPack(buffer, buffer.Position - Frame, size);
                    break;
                }
            }
        }

        protected virtual void OverlapPack(ISegment buffer, int position, int size)
        {
            var count = buffer.Count - position;
            overlapPackOffset = count;
            overlapPackLength = size;
            overlapPackStream ??= BufferPool.Take(config.ReceiveBufferSize + size); //必须是ReceiveBufferSize加大小, 否则上面接收到ReceiveBufferSize大小的时候就会溢出
            overlapPackStream.Write(buffer.Buffer, position, count);
            overlapPack++;
        }

        protected virtual void OnReceiveDataHandler(ISegment buffer, bool isTcp)
        {
            var error = PackageAdapter.Unpack(buffer);
            if (error == -1)
            {
                NDebug.LogError($"[{this}]MD5CRC校验失败!");
                return;
            }
            OnResolveModelHandler(buffer);
        }

        protected virtual void OnResolveModelHandler(ISegment buffer)
        {
            var count = buffer.Count; //记录总长度，在解析每个rpc时不需要复制count，不要改，否则会导致反序列化rpc错误问题
            while (buffer.Position < count)
            {
                var kernelV = buffer.ReadByte();
                var kernel = kernelV == 68;
                if (!kernel & kernelV != 74)
                {
                    ProtocolError++;
                    break;
                }
                var cmd = buffer.ReadByte();
                var token = buffer.ReadUInt32();
                var dataCount = (int)buffer.ReadUInt32Fixed();
                if (buffer.Position + dataCount > count)
                    break;
                var position = buffer.Position + dataCount;
                var model = RpcUnitFactory.Create(cmd: cmd, kernel: kernel, buffer: buffer.Buffer, index: buffer.Position, count: dataCount, token: token);
                if (kernel & IsDeserialize(cmd))
                {
                    buffer.Count = dataCount;
                    buffer.Offset = buffer.Position;
                    var complete = SerializeAdapter.OnDeserializeRpc(buffer, model);
                    if (!complete)
                        goto J;
                }
                trafficStatistics.RpcResolveCount++;
                OnCommand1Handler(model, buffer);//解析协议完成
            J: buffer.Position = position;
            }
        }

        /// <summary>
        /// 区分服务器和客户端是否需要反序列化转发命令的数据
        /// </summary>
        /// <param name="cmd"></param>
        /// <returns></returns>
        protected virtual bool IsDeserialize(byte cmd) => cmd != NetCmd.SceneHash & cmd != NetCmd.NoticeHash & cmd != NetCmd.LocalHash;

        /// <summary>最底层命令处理</summary>
        protected virtual void OnCommand1Handler(IRpcModel model, ISegment segment) => OnCommand2Handler(model, segment);

        /// <summary>第二层命令处理</summary>
        protected virtual void OnCommand2Handler(IRpcModel model, ISegment segment)
        {
            switch (model.Cmd)
            {
                case NetCmd.KeepAliveHash:
                    Call(NetCmd.KeepAliveAck, new byte[1]);
                    break;
                case NetCmd.KeepAliveAckHash:
                    break;
                case NetCmd.PingHash:
                    Call(RpcUnitFactory.Create(cmd: NetCmd.PingCallbackHash, kernel: model.Kernel, buffer: model.GetBuffer, serialize: SyncSerialize.False));
                    break;
                case NetCmd.PingCallbackHash:
                    var ticks = BitConverter.ToUInt32(model.Buffer, model.Index);
                    var delayTime = (uint)Environment.TickCount - ticks;
                    OnPingCallback?.Invoke(delayTime);
                    break;
                case NetCmd.DisconnectHash:
                    Close();
                    break;
                default:
                    OnCommand3Handler(model, segment);
                    break;
            }
        }

        /// <summary>第三层命令处理</summary>
        protected virtual void OnCommand3Handler(IRpcModel model, ISegment segment)
        {
            switch (model.Cmd)
            {
                case NetCmd.UploadHash:
                    DownloadDataHandler(segment);
                    break;
                case NetCmd.DownloadHash:
                    UploadDataHandler((NetCmd)segment.ReadByte(), segment.ReadInt32());
                    break;
                default:
                    OnCommand4Handler(model, segment);
                    break;
            }
        }

        /// <summary>第四层命令处理</summary>
        protected virtual void OnCommand4Handler(IRpcModel model, ISegment segment)
        {
            switch (model.Cmd)
            {
                case NetCmd.LocalHash:
                    Call(RpcUnitFactory.Create(cmd: model.Cmd, kernel: model.Kernel, buffer: model.GetBuffer, serialize: SyncSerialize.False, protocol: model.Protocol, token: model.Token));
                    break;
                default:
                    OnReceiveDataHandler(model, segment);
                    break;
            }
        }

        /// <summary>
        /// 立刻发送, 不需要等待帧时间 (当你要强制把客户端下线时,你还希望客户端先发送完数据后,再强制客户端退出游戏用到)
        /// </summary>
        public virtual void SendDirect() => SendDataHandler();
        #endregion

        #region 客户端发送请求
        public void Call(uint protocol, params object[] pars)
            => Call(NetCmd.CallRpc, protocol, true, SyncSerialize.False, RequestToken.Default, null, pars);
        public void Call(NetCmd cmd, uint protocol, params object[] pars)
            => Call(cmd, protocol, true, SyncSerialize.False, RequestToken.Default, null, pars);
        public void Response(uint protocol, SyncSerialize serialize, in RequestToken token, params object[] pars)
            => Call(NetCmd.CallRpc, protocol, true, serialize, token, null, pars);
        public void Response(uint protocol, in RequestToken token, params object[] pars)
            => Call(NetCmd.CallRpc, protocol, true, SyncSerialize.False, token, null, pars);
        public void Response(NetCmd cmd, uint protocol, in RequestToken token, params object[] pars)
            => Call(cmd, protocol, true, SyncSerialize.False, token, null, pars);

        public void Call(string func, params object[] pars)
            => Call(NetCmd.CallRpc, func.CRCU32(), true, SyncSerialize.False, RequestToken.Default, null, pars);
        public void Call(NetCmd cmd, string func, params object[] pars)
            => Call(cmd, func.CRCU32(), true, SyncSerialize.False, RequestToken.Default, null, pars);
        public void Response(string func, SyncSerialize serialize, in RequestToken token, params object[] pars)
            => Call(NetCmd.CallRpc, func.CRCU32(), true, serialize, token, null, pars);
        public void Response(string func, in RequestToken token, params object[] pars)
            => Call(NetCmd.CallRpc, func.CRCU32(), true, SyncSerialize.False, token, null, pars);
        public void Response(NetCmd cmd, string func, in RequestToken token, params object[] pars)
            => Call(cmd, func.CRCU32(), true, SyncSerialize.False, token, null, pars);

        public void Call(NetCmd cmd, uint protocol, SyncSerialize serialize, in RequestToken token, params object[] pars)
            => Call(cmd, protocol, true, serialize, token, null, pars);

        public void Call(byte[] buffer) => Call(NetCmd.Other, 0, false, SyncSerialize.False, RequestToken.Default, buffer);
        public void Call(NetCmd cmd, byte[] buffer) => Call(cmd, 0, false, SyncSerialize.False, RequestToken.Default, buffer);
        public void Call(NetCmd cmd, byte[] buffer, bool kernel, SyncSerialize serialize) => Call(cmd, 0, kernel, serialize, RequestToken.Default, buffer);
        public virtual void Call(NetCmd cmd, uint protocol, bool kernel, SyncSerialize serialize, in RequestToken token, byte[] buffer, params object[] pars)
        {
            if (buffer != null)
            {
                Call(RpcUnitFactory.Create(cmd: cmd.value, kernel: kernel, buffer: buffer, serialize: serialize, protocol: protocol));
            }
            else
            {
                var model = RpcUnitFactory.Create(cmd: cmd.value, kernel: kernel, protocol: protocol, pars: pars, serialize: new SyncSerialize(!serialize.IsOn), token: token.value);
                if (serialize.IsOn)
                {
                    var segment = BufferPool.Take();
                    SerializeAdapter.OnSerializeRpc(segment, model);
                    model.Buffer = segment.ToArray(true);
                }
                Call(model);
            }
        }

        public void Call(IRpcModel model)
        {
            if (!Connected)
                return;
            if (RpcQueue.Count >= config.LimitQueueCount)
            {
                DataQueueOverflowError++;
                return;
            }
            RpcQueue.Enqueue(model);
        }
        #endregion

        #region 适配器
        /// <summary>
        /// 添加适配器
        /// </summary>
        /// <param name="adapter"></param>
        public void AddAdapter(IAdapter adapter)
        {
            if (adapter is ISerializeAdapter ser)
                AddAdapter(AdapterType.Serialize, ser);
            else if (adapter is IRpcAdapter rpc)
                AddAdapter(AdapterType.Rpc, rpc);
            else if (adapter is IEventAdapter evt)
                AddAdapter(AdapterType.Event, evt);
            else if (adapter is IPackageAdapter package)
                AddAdapter(AdapterType.Package, package);
            else throw new Exception("无法识别的适配器!");
        }

        /// <summary>
        /// 添加适配器
        /// </summary>
        /// <param name="type"></param>
        /// <param name="adapter"></param>
        public void AddAdapter(AdapterType type, IAdapter adapter)
        {
            switch (type)
            {
                case AdapterType.Serialize:
                    SerializeAdapter = (ISerializeAdapter)adapter;
                    break;
                case AdapterType.Rpc:
                    RpcAdapter = (IRpcAdapter)adapter;
                    RpcAdapter.Mode = AdapterMode.Client;
                    break;
                case AdapterType.Event:
                    BindEvent((IEventAdapter)adapter);
                    break;
                case AdapterType.Package:
                    PackageAdapter = (IPackageAdapter)adapter;
                    break;
            }
        }

        public virtual void BindEvent(INetworkHandle network) { }

        public virtual void UnBindEvent(INetworkHandle network) { }
        #endregion

        /// <summary>
        /// 检查心跳
        /// </summary>
        protected virtual bool KeepAliveHandler() => true;

        /// <summary>
        /// 设置心跳时间
        /// </summary>
        /// <param name="timeout">心跳时间间隔, 每interval毫秒会检测一次</param>
        public virtual void SetKeepAliveTimeout(int timeout)
        {
            NetworkLoop.ResetTimeInterval(keepAliveHandlerID, timeout, 2);
        }

        #region 流上下载部分
        /// <summary>
        /// 发送文件, 服务器可以通过重写<see cref="Server.ServerBase{Player, Scene}.OnReceiveFile"/>方法来接收 或 使用事件<see cref="Server.ServerBase{Player, Scene}.OnReceiveFileHandle"/>来监听并处理
        /// </summary>
        /// <param name="filePath"></param>
        /// <param name="bufferSize">每次发送数据大小，如果想最大化发送，你可以设置bufferSize参数为PackageSize - 2048</param>
        /// <returns></returns>
        public bool SendFile(string filePath, int bufferSize = 50000)
        {
            var path1 = Path.GetDirectoryName(filePath);
            if (!Directory.Exists(path1))
            {
                NDebug.LogError("文件不存在! 或者文件路径字符串编码错误! 提示:可以使用Notepad++查看, 编码是ANSI,不是UTF8");
                return false;
            }
            var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
            SendFile(NetCmd.UploadData, fileStream, Path.GetFileName(filePath), bufferSize);
            return true;
        }

        private void SendFile(NetCmd cmd, Stream stream, string name, int bufferSize = 50000)
        {
            var data = new StreamData
            {
                Id = stream.GetHashCode(),
                Name = name,
                Stream = stream,
                bufferSize = bufferSize
            };
            streamDatas.Add(data.Id, data);
            SendFile(cmd, data.Id, data);
        }

        private void SendFile(NetCmd cmd, int fileId, StreamData fileData)
        {
            var stream = fileData.Stream;
            var complete = false;
            long bufferSize = fileData.bufferSize;
            if (stream.Position + fileData.bufferSize >= stream.Length)
            {
                bufferSize = stream.Length - stream.Position;
                complete = true;
            }
            var buffer = new byte[bufferSize];
            stream.Read(buffer, 0, buffer.Length);
            var size = (fileData.Name.Length * 2) + 12;
            var segment = BufferPool.Take((int)bufferSize + size);
            var type = (byte)(fileData.Stream is FileStream ? 0 : 1);
            segment.Write(cmd.value);
            segment.Write(type);
            segment.Write(fileData.Id);
            segment.Write((int)fileData.Stream.Length);
            segment.Write(fileData.Name);
            segment.Write(buffer);
            Call(NetCmd.UploadData, segment.ToArray(true));
            if (type == 0)
                OnSendFileProgressHandler(new StreamProgress(fileData.Name, stream.Position / (float)stream.Length * 100f, complete ? StreamState.Complete : StreamState.Sending));
            if (complete)
            {
                streamDatas.Remove(fileId);
                fileData.Stream.Close();
            }
        }

        public virtual void UploadDataHandler(NetCmd cmd, int fileId)
        {
            if (streamDatas.TryGetValue(fileId, out StreamData data))
                SendFile(cmd, fileId, data);
        }

        public virtual void DownloadDataHandler(ISegment segment)
        {
            var cmd = segment.ReadByte();
            var type = segment.ReadByte();
            var id = segment.ReadInt32();
            var length = segment.ReadInt32();
            var name = segment.ReadString();
            var buffer = segment.ReadByteArray();
            if (!streamDatas.TryGetValue(id, out StreamData data))
            {
                data = new StreamData();
                if (type == 0)
                {
                    string path;
                    if (OnDownloadFile != null)
                    {
                        path = OnDownloadFile(name);
                        var path1 = Path.GetDirectoryName(path);
                        if (!Directory.Exists(path1))
                        {
                            NDebug.LogError("文件不存在! 或者文件路径字符串编码错误! 提示:可以使用Notepad++查看, 编码是ANSI,不是UTF8");
                            return;
                        }
                    }
                    else
                    {
                        int count = 0;
                        string downloadPath;
#if (UNITY_ANDROID || UNITY_IOS) && !UNITY_EDITOR
                        downloadPath = Net.Config.Config.BasePath + "/download/";
#else
                        downloadPath = Environment.CurrentDirectory + "/download/";
#endif
                        if (!Directory.Exists(downloadPath))
                            Directory.CreateDirectory(downloadPath);
                        do
                        {
                            count++;
                            path = downloadPath + $"{name}{count}.temp";
                        }
                        while (File.Exists(path));
                    }
                    data.Stream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
                }
                else
                {
                    data.Stream = new MemoryStream(length);
                }
                data.Id = id;
                data.Name = name;
                streamDatas.Add(id, data);
            }
            data.Stream.Write(buffer, 0, buffer.Length);
            data.Length += buffer.Length;
            if (data.Length < length)
            {
                var len = segment.Count;
                segment.SetPositionLength(0);
                segment.Write(cmd);
                segment.Write(id);
                Call(NetCmd.Download, segment.ToArray(false));
                segment.Count = len;
                OnReceiveFileProgressHandler(new StreamProgress(name, data.Length / (float)length * 100f, StreamState.Download));
                return;
            }
            streamDatas.Remove(id);
            data.Stream.Position = 0;
            if (type == 0)
            {
                OnReceiveFileProgressHandler(new StreamProgress(name, data.Length / (float)length * 100f, StreamState.Complete));
                var isDelete = true;
                if (OnReceiveFile != null)
                    isDelete = OnReceiveFile(data);
                data.Stream.Close();
                if (isDelete)
                    File.Delete((data.Stream as FileStream).Name);
            }
            else
            {
                buffer = new byte[length];
                data.Stream.Read(buffer, 0, length);
                data.Stream.Dispose();
                var model = RpcUnitFactory.Create(cmd: cmd, buffer: buffer);
                OnReceiveDataHandler(model, segment);
            }
        }

        protected virtual void OnSendFileProgressHandler(StreamProgress streamProgress) => OnSendFileProgress?.Invoke(streamProgress);

        protected virtual void OnReceiveFileProgressHandler(StreamProgress streamProgress) => OnReceiveFileProgress?.Invoke(streamProgress);
        #endregion

        /// <summary>
        /// 派发给所有被收集的Rpc方法
        /// </summary>
        /// <param name="protocol"></param>
        /// <param name="pars"></param>
        public void DispatchRpc(uint protocol, params object[] pars)
        {
            DispatchRpc(RpcUnitFactory.Create(cmd: 0, kernel: true, protocol: protocol, pars: pars));
        }

        /// <summary>
        /// 派发给所有被收集的Rpc方法
        /// </summary>
        /// <param name="model"></param>
        public void DispatchRpc(IRpcModel model) => RpcAdapter.Enqueue(this, model);

        /// <summary>
        /// 关闭客户端连接
        /// </summary>
        public virtual void Close() { }

        public override string ToString()
        {
            return $"用户ID:{UserId} IP:{RemotePoint}";
        }
    }
}